"""
资源监控工具 - 系统托盘应用
实现系统托盘图标和菜单功能
"""

import pystray
import tkinter as tk
from PIL import Image
import threading
import time
import sys
import monitor
import traceback
import json
import os
from icon_generator import create_monitor_icon
from main_window import ResourceMonitorApp
from taskbar_display import TaskbarDisplay
from auto_start import AutoStartManager

class SystemTrayApp:
    def __init__(self):
        """初始化系统托盘应用"""
        self.window = None
        self.app = None
        self.window_visible = False
        self.running = True
        
        # 刷新间隔 (秒)
        self.refresh_interval = 2
        
        # 配置文件路径 - 支持PyInstaller打包
        if getattr(sys, 'frozen', False):
            # 如果是PyInstaller打包的exe文件
            application_path = os.path.dirname(sys.executable)
        else:
            # 如果是Python脚本运行
            application_path = os.path.dirname(os.path.abspath(__file__))
        
        self.config_file = os.path.join(application_path, "config.json")
        print(f"配置文件路径: {self.config_file}")
        
        # 初始化开机自启管理器
        self.auto_start_manager = AutoStartManager()
        
        # 加载配置
        self._load_config()
        
        # 先创建一个隐藏的根窗口，用于保持GUI循环
        self._setup_root()
        
        # 创建任务栏显示，传入位置配置和回调函数
        taskbar_config = self.config.get("taskbar_window", {})
        self.taskbar_display = TaskbarDisplay(
            window_config=taskbar_config,
            position_callback=self._on_taskbar_position_changed
        )
        
        # 根据配置显示/隐藏任务栏显示（确保在主线程中执行）
        if self.show_in_taskbar:
            self.root.after(100, self.taskbar_display.show)
            print("根据配置初始化显示任务栏")
        
        # 创建图标
        self.icon = self._create_tray_icon()
        
        # 启动刷新线程
        self.update_thread = threading.Thread(target=self._update_tooltip_thread, daemon=True)
        
    def _setup_root(self):
        """设置隐藏的根窗口"""
        self.root = tk.Tk()
        self.root.withdraw()  # 隐藏根窗口
        
        # 保持应用运行直到明确退出
        self.root.protocol("WM_DELETE_WINDOW", lambda: None)
    
    def run(self):
        """运行系统托盘应用"""
        # 初始化网络速度基准数据
        monitor.get_network_speed()
        
        # 启动刷新线程
        self.update_thread.start()
        
        # 启动托盘图标的线程
        self.tray_thread = threading.Thread(target=self._run_tray, daemon=True)
        self.tray_thread.start()
        
        # 启动主循环
        try:
            self.root.mainloop()
        except KeyboardInterrupt:
            print("接收到键盘中断，退出应用")
            self._exit_app()
        
    def _run_tray(self):
        """在单独线程中运行托盘图标"""
        try:
            self.icon.run()
        except Exception as e:
            print(f"托盘图标运行出错: {e}")
            traceback.print_exc()
            # 托盘图标失败时确保整个应用退出
            self.root.after(100, self.root.destroy)
    
    def _create_window(self):
        """创建或显示主窗口"""
        try:
            if self.window is None or not self.window.winfo_exists():
                print("创建新窗口实例")
                
                # 创建新的Toplevel窗口（使用Toplevel而不是Tk可以解决一些窗口管理问题）
                self.window = tk.Toplevel(self.root)
                self.window.title("资源监控")
                self.window.geometry("300x200")
                self.window.resizable(False, False)
                
                # 确保窗口有标准的装饰（标题栏、关闭按钮等）
                self.window.attributes('-toolwindow', 0)  # 不使用工具窗口风格
                
                # 先隐藏窗口，配置完成后再显示
                self.window.withdraw()
                
                # 创建应用
                self.app = ResourceMonitorApp(self.window)
                
                # 强制更新以确保窗口尺寸正确
                self.window.update_idletasks()
                
                # 绑定关闭按钮事件 - 使用特殊方式确保生效
                def on_close():
                    print("窗口关闭按钮被点击")
                    self._hide_window()
                    return "break"  # 停止事件传播
                
                self.window.protocol("WM_DELETE_WINDOW", on_close)
                
                # 直接绑定到窗口的关闭事件上
                self.window.bind("<Destroy>", lambda e: print("窗口销毁事件被触发"))
                
                # 配置ESC键关闭行为
                self.window.bind("<Escape>", lambda e: self._hide_window())
                
                # 将窗口定位到屏幕右下角
                self._position_window_bottom_right()
                
                # 初始化网络速度基准数据 (确保获取正确的网速)
                monitor.get_network_speed()
                time.sleep(0.1)  # 短暂延迟以初始化基准数据
                
                # 确保应用的更新循环启动
                if self.app:
                    self.app.update_stats(first_run=True)
                
                # 再次确保窗口尺寸是正确的
                self.window.update_idletasks()
                self._position_window_bottom_right()
            else:
                print("使用现有窗口实例")
        except Exception as e:
            print(f"创建窗口时出错: {e}")
            traceback.print_exc()
        
        return self.window
    
    def _position_window_bottom_right(self):
        """将窗口定位到屏幕右下角"""
        if self.window:
            try:
                # 获取屏幕尺寸
                screen_width = self.window.winfo_screenwidth()
                screen_height = self.window.winfo_screenheight()
                
                # 确保窗口尺寸更新
                self.window.update_idletasks()
                
                # 获取窗口尺寸
                window_width = self.window.winfo_width()
                window_height = self.window.winfo_height()
                
                # 防止窗口尺寸为0
                if window_width <= 1 or window_height <= 1:
                    window_width = 300
                    window_height = 200
                
                # 计算右下角坐标
                x = screen_width - window_width - 20
                y = screen_height - window_height - 60
                
                # 设置窗口位置
                self.window.geometry(f"{window_width}x{window_height}+{x}+{y}")
                print(f"设置窗口位置: {window_width}x{window_height}+{x}+{y}")
            except Exception as e:
                print(f"定位窗口时出错: {e}")
    
    def _show_window(self, *args):
        """显示主窗口"""
        print("准备显示窗口...")
        try:
            # 确保在主线程中执行GUI操作
            self.root.after(0, self._show_window_impl)
        except Exception as e:
            print(f"显示窗口时出错: {e}")
            traceback.print_exc()
    
    def _show_window_impl(self):
        """实际执行显示窗口的操作（在主线程中）"""
        try:
            print("执行显示窗口操作")
            # 创建或获取现有窗口
            window = self._create_window()
            
            # 显示窗口前再次确保关闭按钮设置正确
            window.protocol("WM_DELETE_WINDOW", self._hide_window)
            
            # 显示窗口
            self.window_visible = True
            window.deiconify()
            window.lift()
            window.focus_force()
            
            # 强制更新窗口
            window.update()
            
            # 如果窗口之前被隐藏，需要手动触发一次数据更新
            if self.app:
                # 确保应用的数据更新循环正常运行
                self.app.update_stats(first_run=True)
                print("已触发数据更新")
        except Exception as e:
            print(f"显示窗口实现时出错: {e}")
            traceback.print_exc()
    
    def _hide_window(self, *args):
        """隐藏主窗口"""
        try:
            print("隐藏窗口")
            if self.window and self.window.winfo_exists():
                self.window_visible = False
                self.window.withdraw()
                print("窗口已隐藏")
            else:
                print("窗口不存在或已被销毁")
        except Exception as e:
            print(f"隐藏窗口时出错: {e}")
            traceback.print_exc()
    
    def _toggle_window(self, *args):
        """切换窗口显示状态"""
        print(f"切换窗口状态，当前显示状态: {self.window_visible}")
        if self.window_visible:
            self._hide_window()
        else:
            self._show_window()
    
    def _toggle_taskbar_display(self, *args):
        """切换任务栏显示状态"""
        try:
            # 先切换状态变量
            self.show_in_taskbar = not self.show_in_taskbar
            
            # 确保在主线程中执行Tkinter操作
            if self.show_in_taskbar:
                # 使用root.after将操作调度到主线程
                self.root.after(0, self.taskbar_display.show)
                print("已启用任务栏显示")
            else:
                # 使用root.after将操作调度到主线程
                self.root.after(0, self.taskbar_display.hide)
                print("已禁用任务栏显示")
                
            # 保存配置
            self._save_config()
            
            # 更新菜单项文本
            self._update_menu()
        except Exception as e:
            print(f"切换任务栏显示时出错: {e}")
            traceback.print_exc()
    
    def _update_menu(self):
        """更新菜单项文本（例如根据任务栏显示状态）"""
        try:
            # 由于pystray限制，我们不能直接更新菜单文本
            # 但我们可以重新创建菜单并设置到图标
            
            # 创建菜单项
            toggle_window_item = pystray.MenuItem(
                '显示/隐藏窗口',
                self._show_window,
                default=True  # 设为默认项，这样左键点击也会触发此项
            )
            
            # 创建任务栏显示菜单项（根据当前状态设置文本）
            taskbar_text = "关闭任务栏显示" if self.show_in_taskbar else "开启任务栏显示"
            toggle_taskbar_item = pystray.MenuItem(
                taskbar_text,
                self._toggle_taskbar_display
            )
            
            # 创建开机自启菜单项（根据当前状态设置文本）
            auto_start_text = "禁用开机自启" if self.auto_start_enabled else "启用开机自启"
            toggle_auto_start_item = pystray.MenuItem(
                auto_start_text,
                self._toggle_auto_start
            )
            
            exit_item = pystray.MenuItem('退出', self._exit_app)
            
            # 创建新菜单
            menu = pystray.Menu(
                toggle_window_item,
                toggle_taskbar_item,
                toggle_auto_start_item,
                pystray.Menu.SEPARATOR,
                exit_item
            )
            
            # 更新图标菜单
            self.icon.menu = menu
            
        except Exception as e:
            print(f"更新菜单时出错: {e}")
    
    def _exit_app(self, *args):
        """退出应用程序"""
        print("退出应用程序")
        self.running = False
        
        # 保存配置
        self._save_config()
        
        # 关闭任务栏显示
        try:
            if self.taskbar_display:
                # 确保在主线程中关闭任务栏显示
                self.root.after(0, self.taskbar_display.shutdown)
                # 短暂等待确保操作完成
                time.sleep(0.2)
                print("任务栏显示关闭任务已调度")
        except Exception as e:
            print(f"关闭任务栏显示时出错: {e}")
        
        # 关闭托盘图标
        try:
            if self.icon:
                self.icon.stop()
                print("托盘图标已关闭")
        except Exception as e:
            print(f"关闭托盘图标时出错: {e}")
        
        # 销毁窗口
        try:
            if self.window and self.window.winfo_exists():
                self.window.destroy()
                print("窗口已销毁")
        except Exception as e:
            print(f"销毁窗口时出错: {e}")
        
        # 关闭根窗口
        try:
            self.root.quit()
            self.root.destroy()
            print("根窗口已关闭")
        except Exception as e:
            print(f"关闭根窗口时出错: {e}")
        
        # 强制退出（以防万一）
        print("正在完全退出程序...")
    
    def _load_config(self):
        """加载配置"""
        # 默认配置
        self.config = {
            "show_in_taskbar": False,
            "taskbar_window": {
                "x": None,
                "y": None,
                "width": 120,
                "height": 25
            },
            "auto_start": False
        }
        
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, "r", encoding="utf-8") as f:
                    loaded_config = json.load(f)
                    
                    # 合并配置，确保向后兼容性
                    if isinstance(loaded_config, dict):
                        self.config.update(loaded_config)
                        
                        # 确保taskbar_window配置的完整性
                        if "taskbar_window" not in self.config:
                            self.config["taskbar_window"] = {
                                "x": None,
                                "y": None,
                                "width": 120,
                                "height": 25
                            }
                        else:
                            # 确保taskbar_window配置包含所有必要字段
                            default_taskbar = {
                                "x": None,
                                "y": None,
                                "width": 120,
                                "height": 25
                            }
                            default_taskbar.update(self.config["taskbar_window"])
                            self.config["taskbar_window"] = default_taskbar
                        
                        # 确保auto_start配置存在
                        if "auto_start" not in self.config:
                            self.config["auto_start"] = False
                            
                print(f"已加载配置: {self.config}")
        except Exception as e:
            print(f"加载配置时出错: {e}")
            traceback.print_exc()
        
        # 从配置中设置实例变量
        self.show_in_taskbar = self.config.get("show_in_taskbar", False)
        self.auto_start_enabled = self.config.get("auto_start", False)
        
        # 检查开机自启的实际状态并同步配置
        self._sync_auto_start_status()
    
    def _save_config(self):
        """保存配置"""
        try:
            # 更新配置字典
            self.config["show_in_taskbar"] = self.show_in_taskbar
            self.config["auto_start"] = self.auto_start_enabled
            
            # 获取任务栏窗口的当前位置并保存
            if self.taskbar_display:
                current_position = self.taskbar_display.get_current_position()
                if current_position:
                    self.config["taskbar_window"] = current_position
                    print(f"保存任务栏窗口位置: {current_position}")
            
            # 保存到文件
            with open(self.config_file, "w", encoding="utf-8") as f:
                json.dump(self.config, f, ensure_ascii=False, indent=4)
            
            print(f"已保存配置: {self.config}")
        except Exception as e:
            print(f"保存配置时出错: {e}")
            traceback.print_exc()
    
    def _on_taskbar_position_changed(self, position):
        """处理任务栏窗口位置变化的回调
        
        Args:
            position (dict): 包含x, y, width, height的位置信息
        """
        try:
            # 更新配置中的位置信息
            self.config["taskbar_window"] = position
            
            # 异步保存配置（避免频繁的文件操作）
            self.root.after(100, self._save_config_delayed)
            
        except Exception as e:
            print(f"处理任务栏位置变化时出错: {e}")
            traceback.print_exc()
    
    def _save_config_delayed(self):
        """延迟保存配置（防止频繁保存）"""
        try:
            with open(self.config_file, "w", encoding="utf-8") as f:
                json.dump(self.config, f, ensure_ascii=False, indent=4)
            print("配置已异步保存")
        except Exception as e:
            print(f"异步保存配置时出错: {e}")
    
    def _sync_auto_start_status(self):
        """同步开机自启状态
        
        检查注册表中的实际开机自启状态，并与配置文件同步
        """
        try:
            # 获取注册表中的实际状态
            actual_status = self.auto_start_manager.is_auto_start_enabled()
            
            # 如果配置文件中的状态与实际状态不一致，以实际状态为准
            if self.auto_start_enabled != actual_status:
                print(f"开机自启状态不同步: 配置={self.auto_start_enabled}, 实际={actual_status}")
                self.auto_start_enabled = actual_status
                self.config["auto_start"] = actual_status
                # 不立即保存配置，避免启动时的不必要写入
                
        except Exception as e:
            print(f"同步开机自启状态时出错: {e}")
    
    def _toggle_auto_start(self, *args):
        """切换开机自启状态"""
        try:
            print(f"切换开机自启状态，当前状态: {self.auto_start_enabled}")
            
            # 切换状态
            success, new_status = self.auto_start_manager.toggle_auto_start()
            
            if success:
                self.auto_start_enabled = new_status
                self.config["auto_start"] = new_status
                
                # 保存配置
                self._save_config()
                
                # 更新菜单
                self._update_menu()
                
                status_text = "已启用" if new_status else "已禁用"
                print(f"开机自启{status_text}")
                
            else:
                print("切换开机自启状态失败")
                
        except Exception as e:
            print(f"切换开机自启状态时出错: {e}")
            traceback.print_exc()
    
    def _create_tray_icon(self):
        """创建系统托盘图标"""
        # 生成图标
        icon_image = create_monitor_icon(64)
        
        try:
            # 创建菜单项
            toggle_window_item = pystray.MenuItem(
                '显示/隐藏窗口', 
                self._show_window,
                default=True  # 设为默认项，这样左键点击也会触发此项
            )
            
            # 创建任务栏显示菜单项（根据当前状态设置文本）
            taskbar_text = "关闭任务栏显示" if self.show_in_taskbar else "开启任务栏显示"
            toggle_taskbar_item = pystray.MenuItem(
                taskbar_text,
                self._toggle_taskbar_display
            )
            
            # 创建开机自启菜单项（根据当前状态设置文本）
            auto_start_text = "禁用开机自启" if self.auto_start_enabled else "启用开机自启"
            toggle_auto_start_item = pystray.MenuItem(
                auto_start_text,
                self._toggle_auto_start
            )
            
            exit_item = pystray.MenuItem('退出', self._exit_app)
            
            # 创建菜单
            menu = pystray.Menu(
                toggle_window_item,
                toggle_taskbar_item,
                toggle_auto_start_item,
                pystray.Menu.SEPARATOR,
                exit_item
            )
            
            # 创建托盘图标
            icon = pystray.Icon(
                "resource_monitor",
                icon=icon_image,
                title="资源监控\n(左键点击显示/隐藏窗口)",
                menu=menu
            )
            
            # 设置左键点击行为（注意：在某些平台上这可能无效）
            def on_activate(icon):
                print("托盘图标被激活（左键点击）")
                self._toggle_window()
                
            icon.on_activate = on_activate
            
            return icon
            
        except Exception as e:
            print(f"创建托盘图标出错: {e}")
            traceback.print_exc()
            
            # 创建一个最基本的图标，确保程序能运行
            basic_icon = pystray.Icon(
                "resource_monitor",
                icon=icon_image,
                title="资源监控 (基本模式)"
            )
            return basic_icon
    
    def _update_tooltip_thread(self):
        """更新托盘图标提示文本的线程"""
        # 初始化上一次网络速度基准
        monitor.get_network_speed()
        time.sleep(1)
        
        while self.running:
            try:
                # 获取资源数据
                cpu_percent = monitor.get_cpu_usage()
                mem_percent = monitor.get_memory_usage()
                upload, download = monitor.get_network_speed()
                
                # 更新托盘图标提示文本
                tooltip = (
                    f"CPU: {cpu_percent:.1f}%\n"
                    f"内存: {mem_percent:.1f}%\n"
                    f"上传: {upload}\n"
                    f"下载: {download}\n"
                    f"左键点击: 显示/隐藏窗口\n"
                    f"右键点击: 显示菜单"
                )
                
                # 安全地更新提示文本
                try:
                    if hasattr(self.icon, 'title'):
                        self.icon.title = tooltip
                except Exception as e:
                    print(f"更新托盘提示时出错: {e}")
                
                # 等待指定时间
                time.sleep(self.refresh_interval)
            except Exception as e:
                print(f"更新托盘提示信息循环出错: {e}")
                time.sleep(self.refresh_interval)

# 直接运行此文件时
if __name__ == "__main__":
    try:
        print("启动托盘应用...")
        tray_app = SystemTrayApp()
        tray_app.run()
    except Exception as e:
        print(f"托盘应用运行出错: {e}")
        traceback.print_exc() 